﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos.Weaver;
using PostSharp.CodeModel;
using PostSharp.CodeWeaver;
using PostSharp.Collections;

namespace EntityFrameworkContrib.PostSharp4EF.Weaver.EDMAttributes
{
    /// <summary>
    /// will implement calls on the .ctor to iniatialize properties to the default values
    /// </summary>
  internal class DefaultValueAdvice : ITypeLevelAdvice
  {
    Dictionary<FieldDefDeclaration, object> FieldsNeedingDefaultValue;
    TypeDefDeclaration targetType;

    public DefaultValueAdvice(Dictionary<FieldDefDeclaration, object> FieldsNeedingDefaultValue, TypeDefDeclaration targetType)
    {
      this.FieldsNeedingDefaultValue = FieldsNeedingDefaultValue;
      this.targetType = targetType;
    }

    #region weaving
    public void Weave(PostSharp.CodeWeaver.WeavingContext context, InstructionBlock block)
    {
      foreach (FieldDefDeclaration field in FieldsNeedingDefaultValue.Keys)
      {
        object value = FieldsNeedingDefaultValue[field];

        if (value == null)
          continue;

        // the context is the ctor because we only use the joinpoint AfterinstanceInitialization
        InstructionSequence sequence = context.Method.MethodBody.CreateInstructionSequence();
        block.AddInstructionSequence(sequence, NodePosition.Before, null);
        context.InstructionWriter.AttachInstructionSequence(sequence);
        InstructionWriter writer = context.InstructionWriter;

        if (value is int)
        {
          writer.EmitInstruction(OpCodeNumber.Nop);
          writer.EmitInstruction(OpCodeNumber.Ldarg_0);
          writer.EmitInstructionInt32(OpCodeNumber.Ldc_I4, (int)value);
          writer.EmitInstructionField(OpCodeNumber.Stfld, field);
        }
        else if (value is string)
        {
          writer.EmitInstruction(OpCodeNumber.Nop);
          writer.EmitInstruction(OpCodeNumber.Ldarg_0);
          writer.EmitInstructionString(OpCodeNumber.Ldstr, (string)value);
          writer.EmitInstructionField(OpCodeNumber.Stfld, field);
        }
        else
        {
          // TODO: implement other value types
          throw new NotImplementedException(String.Format("No IL default implemented for type {0}", value.GetType()));
        }

        writer.DetachInstructionSequence(true);
      }

    }
    #endregion

    #region ITypeLevelAdvice Members

    public JoinPointKinds JoinPointKinds { get { return JoinPointKinds.AfterInstanceInitialization; } }


    public TypeDefDeclaration Type { get { return this.targetType; } }


    #endregion

    #region IAdvice Members

    public int Priority
    {
      get { return 0; }
    }

    public bool RequiresWeave(PostSharp.CodeWeaver.WeavingContext context)
    {
      return true;
    }

    #endregion
  }
}
